package com.clj.fastble.scan;

import com.clj.fastble.callback.BleScanPresenterImp;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.data.BleMsg;
import com.clj.fastble.utils.BleLog;
import com.clj.fastble.utils.HexUtil;

import ohos.app.Context;
import ohos.bluetooth.ble.BleCentralManagerCallback;
import ohos.bluetooth.ble.BleScanResult;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * BleScanPresenter
 */
public abstract class BleScanPresenter implements BleCentralManagerCallback {
    private boolean mNeedConnect;
    private long mScanTimeout;
    private BleScanPresenterImp mBleScanPresenterImp;

    private List<BleDevice> mBleDeviceList = new ArrayList<>();

    private EventHandler mMainHandler = new EventHandler(EventRunner.getMainEventRunner());
    private EventRunner mHandlerThread;
    private EventHandler mHandler;
    private boolean mHandling;
    private Context context;

    private static final class ScanHandler extends EventHandler {
        private final WeakReference<BleScanPresenter> mBleScanPresenter;

        ScanHandler(EventRunner looper, BleScanPresenter bleScanPresenter) {
            super(looper);
            mBleScanPresenter = new WeakReference<>(bleScanPresenter);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            BleScanPresenter bleScanPresenter = mBleScanPresenter.get();
            if (bleScanPresenter != null) {
                if (event.eventId == BleMsg.MSG_SCAN_DEVICE) {
                    final BleDevice bleDevice = (BleDevice) event.object;
                    BleLog.info("bleDevice", bleDevice.getName() + "");

                    if (bleDevice != null) {
                        bleScanPresenter.handleResult(bleDevice);
                    }
                }
            }
        }
    }

    private void handleResult(final BleDevice bleDevice) {
        mMainHandler.postTask(
                new Runnable() {
                    @Override
                    public void run() {
                        onLeScan(bleDevice);
                    }
                });
        checkDevice(bleDevice);
    }

    /**
     * ble prepare scan
     * @param mcontext mcontext
     * @param needConnect needConnect
     * @param timeOut timeOut
     * @param bleScanPresenterImp bleScanPresenterImp
     */
    public void prepare(Context mcontext, boolean needConnect, long timeOut, BleScanPresenterImp bleScanPresenterImp) {
        context = mcontext;
        mNeedConnect = needConnect;
        mScanTimeout = timeOut;
        mBleScanPresenterImp = bleScanPresenterImp;

        mHandlerThread = EventRunner.create(BleScanPresenter.class.getSimpleName());
        mHandler = new ScanHandler(mHandlerThread, this);
        mHandling = true;
    }

    /**
     * ismNeedConnect
     * @return boolean
     */
    public boolean ismNeedConnect() {
        return mNeedConnect;
    }

    /**
     * getBleScanPresenterImp
     * @return BleScanPresenterImp
     */
    public BleScanPresenterImp getBleScanPresenterImp() {
        return mBleScanPresenterImp;
    }

    @Override
    public void scanResultEvent(BleScanResult bleScanResult) {
        Map<java.lang.Integer, byte[]> manu = bleScanResult.getManufacturerData();
        int key = 0;
        for (int keys : manu.keySet()) {
            key = keys;
        }
        BleLog.info("BleScanResult", key + "---3");
        byte[] manuByte = manu.get(key);
        String manufacturerStr = HexUtil.formatHexString(manuByte, true);
        BleLog.info("BleScanResult", manufacturerStr + "---4");

        if (bleScanResult == null) {
            return;
        }

        if (!mHandling) {
            return;
        }

        InnerEvent event = InnerEvent.get(BleMsg.MSG_SCAN_DEVICE);
        event.object =
                new BleDevice(
                        bleScanResult.getPeripheralDevice(),
                        bleScanResult.getRssi(),
                        manuByte,
                        manufacturerStr,
                        bleScanResult.getTime(),
                        bleScanResult.isConnectable(),
                        bleScanResult.getAdvertiseFlag());

        mHandler.sendEvent(event);
    }

    private void checkDevice(BleDevice bleDevice) {
        correctDeviceAndNextStep(bleDevice);
    }

    private void correctDeviceAndNextStep(final BleDevice bleDevice) {
        if (mNeedConnect) {
            BleLog.info(
                    "devices detected  ------",
                    "  name:" + bleDevice.getName() + "  mac:" + bleDevice.getMac() + "  Rssi:" + bleDevice.getRssi());

            mBleDeviceList.add(bleDevice);
            mMainHandler.postTask(
                    new Runnable() {
                        @Override
                        public void run() {
                            BleScanner.getInstance().stopLeScan();
                        }
                    });

        } else {
            AtomicBoolean hasFound = new AtomicBoolean(false);
            for (BleDevice result : mBleDeviceList) {
                if (result.getDevice().equals(bleDevice.getDevice())) {
                    hasFound.set(true);
                }
            }
            if (!hasFound.get()) {
                BleLog.info(
                        "device detected  ------",
                        "  name: "
                                + bleDevice.getName()
                                + "  mac: "
                                + bleDevice.getMac()
                                + "  Rssi: "
                                + bleDevice.getRssi());

                mBleDeviceList.add(bleDevice);
                mMainHandler.postTask(
                        new Runnable() {
                            @Override
                            public void run() {
                                onScanning(bleDevice);
                            }
                        });
            }
        }
    }

    /**
     * notifyScanStarted
     * @param success success
     */
    public final void notifyScanStarted(final boolean success) {
        mBleDeviceList.clear();

        removeHandlerMsg();

        if (success && mScanTimeout > 0) {
            mMainHandler.postTask(
                    new Runnable() {
                        @Override
                        public void run() {
                            BleScanner.getInstance().stopLeScan();
                        }
                    },
                    mScanTimeout);
        }

        mMainHandler.postTask(
                new Runnable() {
                    @Override
                    public void run() {
                        onScanStarted(success);
                    }
                });
    }

    /**
     * notifyScanStopped
     */
    public final void notifyScanStopped() {
        mHandling = false;
        removeHandlerMsg();
        mMainHandler.postTask(
                new Runnable() {
                    @Override
                    public void run() {
                        onScanFinished(mBleDeviceList);
                    }
                });
    }

    /**
     * removeHandlerMsg
     */
    public final void removeHandlerMsg() {
        mMainHandler.removeAllEvent();
        mHandler.removeAllEvent();
    }

    /**
     * onScanStarted
     * @param success success
     */
    public abstract void onScanStarted(boolean success);

    /**
     * onLeScan
     * @param bleDevice bleDevice
     */
    public abstract void onLeScan(BleDevice bleDevice);

    /**
     * onScanning
     * @param bleDevice bleDevice
     */
    public abstract void onScanning(BleDevice bleDevice);

    /**
     * onScanFinished
     * @param bleDeviceList bleDeviceList
     */
    public abstract void onScanFinished(List<BleDevice> bleDeviceList);
}
